#include <gtest/gtest.h>
#include <behaviortree_cpp_v3/behavior_tree.h>
#include <behaviortree_cpp_v3/bt_factory.h>
#include <memory>
#include <logger/logger.h>

class AAA: public BT::StatefulActionNode
{
public:
    AAA(const std::string &name, const BT::NodeConfiguration &configuration)
        : BT::StatefulActionNode(name, configuration)
    {
    }
    ~AAA() override = default;

    static BT::PortsList providedPorts()
    {
        return {};
    }
    BT::NodeStatus onCancel() override
    {
        return BT::NodeStatus::SUCCESS;
    }
    BT::NodeStatus onPause() override
    {
        return BT::NodeStatus::SUCCESS;
    }
    BT::NodeStatus onResume() override
    {
        return BT::NodeStatus::SUCCESS;
    }
    BT::NodeStatus onStart() override
    {
        EASE_INFO("%s, AA onStart", name().c_str());
        return BT::NodeStatus::RUNNING;
    }
    BT::NodeStatus onRunning() override
    {
        static int count = 0;
        if (count >= 3)
        {
            EASE_INFO("aaa interpret done");
            return BT::NodeStatus::SUCCESS;
        }
        count++;
        EASE_INFO("aaa running");
        return BT::NodeStatus::RUNNING;
    }
    void onHalted() override
    {
        EASE_INFO("%s, AA onHalted", name().c_str());
    }
private:
};

class BBB: public BT::StatefulActionNode
{
public:
    BBB(const std::string &name, const BT::NodeConfiguration &configuration)
        : BT::StatefulActionNode(name, configuration)
    {
    }
    ~BBB() override = default;

    static BT::PortsList providedPorts()
    {
        return {};
    }
    BT::NodeStatus onStart() override
    {
        EASE_INFO("bbb start");
        return BT::NodeStatus::RUNNING;
    }
    BT::NodeStatus onRunning() override
    {
        static int count = 0;
        if (count >= 3)
        {
            EASE_INFO("bbb interpret done");
            return BT::NodeStatus::SUCCESS;
        }
        count++;
        EASE_INFO("bbb running");
        return BT::NodeStatus::RUNNING;
    }
    BT::NodeStatus onPause() override
    {
        return BT::NodeStatus::SUCCESS;
    }
    BT::NodeStatus onResume() override
    {
        return BT::NodeStatus::SUCCESS;
    }
    BT::NodeStatus onCancel() override
    {
        return BT::NodeStatus::SUCCESS;
    }
    void onHalted() override
    {

    }
};
class CCC: public BT::StatefulActionNode
{
public:
    CCC(const std::string &name, const BT::NodeConfiguration &configuration)
        : BT::StatefulActionNode(name, configuration)
    {
    }
    ~CCC() override = default;

    static BT::PortsList providedPorts()
    {
        return {};
    }
    BT::NodeStatus onStart() override
    {
        EASE_INFO("ccc start");
        return BT::NodeStatus::SUCCESS;
    }
    BT::NodeStatus onRunning() override
    {
        EASE_INFO("ccc running");
        return BT::NodeStatus::SUCCESS;
    }
    BT::NodeStatus onPause() override
    {
        return BT::NodeStatus::SUCCESS;
    }
    BT::NodeStatus onResume() override
    {
        return BT::NodeStatus::SUCCESS;
    }
    BT::NodeStatus onCancel() override
    {
        return BT::NodeStatus::SUCCESS;
    }
    void onHalted() override
    {

    }
};

class DDD: public BT::SyncActionNode
{
public:
    DDD(const std::string &name, const BT::NodeConfiguration &configuration)
        : BT::SyncActionNode(name, configuration)
    {
    }
    ~DDD() override = default;

    static BT::PortsList providedPorts()
    {
        return {};
    }
    BT::NodeStatus tick() override
    {
        static int count = 0;
        if (count >= 10)
        {
            EASE_INFO("ddd interpret done");
            return BT::NodeStatus::SUCCESS;
        }
        count++;
        EASE_INFO("ddd running");
        return BT::NodeStatus::SUCCESS;
    }
};

class EEE: public BT::SyncActionNode
{
public:
    EEE(const std::string &name, const BT::NodeConfiguration &configuration)
        : BT::SyncActionNode(name, configuration)
    {
    }
    ~EEE() override = default;

    static BT::PortsList providedPorts()
    {
        return {};
    }
    BT::NodeStatus tick() override
    {
        static int count = 0;
        if (count >= 10)
        {
            EASE_INFO("eee interpret done");
            return BT::NodeStatus::SUCCESS;
        }
        count++;
        EASE_INFO("eee running");
        return BT::NodeStatus::SUCCESS;
    }
};

TEST(test_class_async_sequence_xml, behavior_tree_class)
{
    BT::BehaviorTreeFactory factory;
    factory.registerNodeType<AAA>("aaa");

    const char *xml_text = R"(
        <root BTCPP_format="3">
             <BehaviorTree>
                <Sequence>
                    <aaa/>
                </Sequence>
             </BehaviorTree>
         </root>
    )";

    BT::Tree tree = factory.createTreeFromText(xml_text);
    tree.tickRootWhileRunning();
    EASE_INFO("done");
}

TEST(test_class_async_sequence_no_xml, behavior_tree_class)
{
    std::unique_ptr<AAA> aa = std::make_unique<AAA>("111", BT::NodeConfiguration{});
    std::unique_ptr<AAA> aa2 = std::make_unique<AAA>("222", BT::NodeConfiguration{});
    std::unique_ptr<AAA> aa3 = std::make_unique<AAA>("333", BT::NodeConfiguration{});

    BT::SequenceNode sequence_node("1");
    sequence_node.addChild(aa.get());
//    sequence_node.addChild(aa3.get());

    BT::SequenceNode sequence_node2("2");
    sequence_node2.addChild(&sequence_node);
    sequence_node2.addChild(aa2.get());

    BT::NodeStatus status = sequence_node.executeTick();
    while (status == BT::NodeStatus::RUNNING)
    {
        status = sequence_node.executeTick();
        break;
    }
    sequence_node.halt();
    sequence_node.executeTick();
    sequence_node.halt();
    sequence_node.executeTick();
    EASE_INFO("done");
}
TEST(behavior_tree_while_do_else_func, behavior_tree)
{
    const char *xml_text_while_do_else_func = R"(
        <root  BTCPP_format="4">
            <BehaviorTree>
                <WhileDoElse>
                    <Sequence>
                        <EE/>
                        <AA/>
                    </Sequence>
                    <BB/>
                    <CC/>
                </WhileDoElse>
            </BehaviorTree>
        </root>
        )";

    BT::BehaviorTreeFactory factory;
    factory.registerNodeType<AAA>("AA");
    factory.registerNodeType<BBB>("BB");
    factory.registerNodeType<CCC>("CC");
    factory.registerNodeType<DDD>("DD");
    factory.registerNodeType<EEE>("EE");

    BT::Tree tree = factory.createTreeFromText(xml_text_while_do_else_func);

    BT::NodeStatus status = tree.tickOnce();

    while (status == BT::NodeStatus::RUNNING)
    {
        tree.sleep(std::chrono::milliseconds(500));
        status = tree.tickOnce();
    }
}

TEST(behavior_tree_delay_class_sync, behavior_tree)
{
    const char *xml_text_while_do_else_func = R"(
        <root  BTCPP_format="4">
            <BehaviorTree>
                <Delay delay_msec="5000">
                    <AA/>
                </Delay>
            </BehaviorTree>
        </root>
        )";

    BT::BehaviorTreeFactory factory;
    factory.registerNodeType<AAA>("AA");
    factory.registerNodeType<BBB>("BB");
    factory.registerNodeType<CCC>("CC");

    BT::Tree tree = factory.createTreeFromText(xml_text_while_do_else_func);
    BT::NodeStatus status = tree.tickOnce();
    while (status == BT::NodeStatus::RUNNING)
    {
        tree.sleep(std::chrono::milliseconds(500));
        status = tree.tickOnce();
    }
    EASE_INFO("done");
}

BT::NodeStatus checkBattery()
{
    EASE_INFO("Battery ok");
    return BT::NodeStatus::SUCCESS;
}

TEST(behavior_tree_rective_sequence_class_sync, behavior_tree)
{
    const char *xml = R"(
        <root BTCPP_format="4">
         <BehaviorTree>
            <ReactiveSequence>
                <Fallback>
                    <EE/>
                    <DD/>
                </Fallback>
                <AA/>
                <BB/>
            </ReactiveSequence>
         </BehaviorTree>
        </root>
        )";

    BT::BehaviorTreeFactory factory;
    factory.registerNodeType<AAA>("AA");
    factory.registerNodeType<BBB>("BB");
    factory.registerNodeType<CCC>("CC");
    factory.registerNodeType<DDD>("DD");
    factory.registerNodeType<EEE>("EE");

    BT::Tree tree = factory.createTreeFromText(xml);
    BT::NodeStatus status = tree.tickOnce();
    while (status == BT::NodeStatus::RUNNING)
    {
        tree.sleep(std::chrono::milliseconds(500));
        status = tree.tickOnce();
    }
    EASE_INFO("done");
}
TEST(behavior_tree_rective_fallback_class_sync, behavior_tree)
{
    const char *xml = R"(
        <root BTCPP_format="4">
         <BehaviorTree>
            <ReactiveFallback>
                <DD/>
                <BB/>
                <EE/>
                <AA/>
            </ReactiveFallback>
         </BehaviorTree>
        </root>
        )";

    BT::BehaviorTreeFactory factory;
    factory.registerNodeType<AAA>("AA");
    factory.registerNodeType<BBB>("BB");
    factory.registerNodeType<CCC>("CC");
    factory.registerNodeType<DDD>("DD");
    factory.registerNodeType<EEE>("EE");

    BT::Tree tree = factory.createTreeFromText(xml);
    BT::NodeStatus status = tree.tickOnce();
    while (status == BT::NodeStatus::RUNNING)
    {
        tree.sleep(std::chrono::milliseconds(500));
        status = tree.tickOnce();
    }
    EASE_INFO("done");
}
